package com.summerbird.mallchat.common.chat.service.impl;

import com.summerbird.mallchat.common.chat.dao.*;
import com.summerbird.mallchat.common.chat.domain.entity.GroupMember;
import com.summerbird.mallchat.common.chat.domain.entity.Room;
import com.summerbird.mallchat.common.chat.domain.entity.RoomGroup;
import com.summerbird.mallchat.common.chat.domain.vo.request.admin.AdminAddReq;
import com.summerbird.mallchat.common.chat.domain.vo.request.admin.AdminRevokeReq;
import com.summerbird.mallchat.common.chat.domain.vo.request.member.MemberExitReq;
import com.summerbird.mallchat.common.chat.service.IGroupMemberService;
import com.summerbird.mallchat.common.chat.service.cache.GroupMemberCache;
import com.summerbird.mallchat.common.chat.service.cache.RoomCache;
import com.summerbird.mallchat.common.chat.service.cache.RoomGroupCache;
import com.summerbird.mallchat.common.common.exception.GroupErrorEnum;
import com.summerbird.mallchat.common.common.utils.AssertUtil;
import com.summerbird.mallchat.common.user.service.impl.PushService;
import com.summerbird.mallchat.common.websocket.domain.vo.response.WSBaseResp;
import com.summerbird.mallchat.common.websocket.domain.vo.response.WSMemberChange;
import com.summerbird.mallchat.common.websocket.service.adapter.WebSocketAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Slf4j
@Service
public class IGroupMemberServiceImpl implements IGroupMemberService {

    private static final int MAX_MANAGE_COUNT = 3;
    @Autowired
    private RoomDao roomDao;
    @Autowired
    private RoomGroupDao roomGroupDao;
    @Autowired
    private GroupMemberDao groupMemberDao;
    @Autowired
    private ContactDao contactDao;
    @Autowired
    private MessageDao messageDao;
    @Autowired
    private RoomGroupCache roomGroupCache;
    @Autowired
    private RoomCache roomCache;
    @Autowired
    private GroupMemberCache groupMemberCache;
    @Autowired
    private PushService pushService;

    @Override
    public void addAdmin(Long uid, AdminAddReq request) {
        List<Long> uidList = request.getUidList();
        // 1. 判断群聊是否存在
        Room room = roomCache.get(request.getRoomId());
        AssertUtil.isNotEmpty(room, "房间id有误");
        RoomGroup roomGroup = roomGroupDao.getByRoomId(request.getRoomId());
        AssertUtil.isNotEmpty(roomGroup, GroupErrorEnum.GROUP_NOT_EXIST);
        // 2. 判断该用户是否是群主
        AssertUtil.isTrue(groupMemberDao.isLord(roomGroup.getId(), uid), GroupErrorEnum.NOT_ALLOWED_OPERATION);
        // 3. 判断群成员是否在群中
        Boolean isGroupShip = groupMemberDao.isGroupShip(roomGroup.getRoomId(), uidList);
        AssertUtil.isTrue(isGroupShip, GroupErrorEnum.USER_NOT_IN_GROUP);
        // 4. 判断管理员数量是否达到上限
        // 4.1 查询现有管理员数量
        List<Long> managerMembers = groupMemberDao.getManagerMembers(roomGroup.getId());
        // 4.2 去重
        HashSet<Long> manageUidSet = new HashSet<>(managerMembers);
        manageUidSet.addAll(uidList);
        AssertUtil.isFalse(manageUidSet.size() > MAX_MANAGE_COUNT, GroupErrorEnum.MANAGE_COUNT_EXCEED);
        // 5. 增加管理员
        groupMemberDao.addAdmin(roomGroup.getId(), uidList);
    }

    @Override
    public void revokeAdmin(Long uid, AdminRevokeReq request) {
        List<Long> uidList = request.getUidList();
        // 1. 判断群聊是否存在
        RoomGroup roomGroup = roomGroupCache.get(request.getRoomId());
        AssertUtil.isNotEmpty(roomGroup, GroupErrorEnum.GROUP_NOT_EXIST);
        // 2. 判断是否是群主
        AssertUtil.isTrue(groupMemberDao.isLord(roomGroup.getId(), uid), GroupErrorEnum.NOT_ALLOWED_OPERATION);
        // 3. 判断群成员是否在群组中
        Boolean isGroupShip = groupMemberDao.isGroupShip(roomGroup.getRoomId(), request.getUidList());
        AssertUtil.isTrue(isGroupShip, GroupErrorEnum.USER_NOT_IN_GROUP);
        // 4. 撤销管理员
        groupMemberDao.removeAdmin(roomGroup.getId(), uidList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void exitGroup(Long uid, MemberExitReq request) {
        // 1. 判断群聊是否存在
        RoomGroup roomGroup = roomGroupCache.get(request.getRoomId());
        AssertUtil.isNotEmpty(roomGroup, GroupErrorEnum.GROUP_NOT_EXIST);
        // 2. 判断房间是否是大群聊 （大群聊禁止退出）
        Room room = roomCache.get(request.getRoomId());
        AssertUtil.isFalse(room.isHotRoom(), GroupErrorEnum.NOT_ALLOWED_FOR_EXIT_GROUP);
        // 3. 判断群成员是否在群中
        GroupMember member = groupMemberDao.getMember(roomGroup.getId(), uid);
        AssertUtil.isNotEmpty(member, GroupErrorEnum.USER_NOT_IN_GROUP);
        // 4. 判断该用户是否是群主
        boolean isLord = groupMemberDao.isLord(roomGroup.getId(), uid);
        // 群主则删群
        if(isLord){
            // 4.1 删除房间
            roomDao.removeById(room.getId());
            // 4.2 删除会话
            contactDao.removeByRoomId(room.getId(), Collections.singletonList(uid));
            // 4.3 删除群成员
            groupMemberDao.removeByGroupId(roomGroup.getId(), Collections.EMPTY_LIST);
            // 4.4 删除消息记录 (逻辑删除)
            messageDao.removeByRoomId(room.getId(), Collections.EMPTY_LIST);
            // TODO 这里也可以告知群成员 群聊已被删除的消息
        }
        // 非群主则退出
        else {
            // 4.5 删除会话
            contactDao.removeByRoomId(room.getId(), Collections.singletonList(uid));
            // 4.6 删除群成员
            groupMemberDao.removeByGroupId(roomGroup.getId(), Collections.singletonList(uid));
            // 4.7 发送移除事件告知群成员
            List<Long> memberUidList = groupMemberCache.getMemberUidList(roomGroup.getRoomId());
            WSBaseResp<WSMemberChange> wsBaseResp = WebSocketAdapter.buildMemberRemoveWS(room.getId(), uid);
            pushService.sendPushMsg(wsBaseResp, memberUidList);
            // 清除缓存
            groupMemberCache.evictMemberUidList(room.getId());
        }
    }
}
